package com.chancein.cloud.ocr.process.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.chancein.cloud.ocr.process.domain.OcrExcelData;
import com.chancein.cloud.ocr.process.domain.dto.HsOrcBody;
import com.chancein.cloud.ocr.process.domain.dto.OcrClassifyDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 恒生OCR工具类
 *
 * @author: LiaoZiHao
 * @date: 2023/8/8 19:06
 * Copyright © 2022 Hundsun Technologies Inc. All Rights Reserved
 */
@Slf4j
public class HsOcrUtils {
    private static final String HS_OCR_SUCCESS = "00";
    private static final String hsOcrClassifyUrl = "http://10.5.23.190:50003/ids/hschart/classify";
    private static final String hsOcrUrl = "http://10.5.23.190:50003/ids/hschart";
    public static String picOcrUrl = "http://10.5.14.242:10199/getPicData/";

    public static String getHsOcrUrl() {
        return hsOcrUrl;
    }

    public static String getPicOcrUrl() {
        return picOcrUrl;
    }

    public static OcrClassifyDto hsChartClassify(InputStream inputStream, String suffix) {
        HsOrcBody hsOrcBody = imageAnalyticalStream(inputStream, suffix, hsOcrClassifyUrl);
        String code = Optional.ofNullable(hsOrcBody).orElse(new HsOrcBody()).getCode();
        OcrClassifyDto ocrClassifyDto = new OcrClassifyDto();
        ocrClassifyDto.setOkFlag(StringUtils.isNoneBlank(code) && HS_OCR_SUCCESS.equals(code));
        ocrClassifyDto.setBody(hsOrcBody);
        return ocrClassifyDto;
    }

    public static HsOrcBody imageAnalyticalStream(InputStream inputStream, String suffix, String restUrl) {
        if (StringUtils.isBlank(hsOcrClassifyUrl)) {
            return null;
        }
        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置请求头格式
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // 构建请求参数
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {
            private int length;

            /**
             * 覆写父类方法
             * 如果不重写这个方法，并且文件有一定大小，那么服务端会出现异常
             * {@code The multi-part request contained parameter data (excluding uploaded files) that exceeded}
             *
             * @return
             */
            @Override
            public String getFilename() {
                return "temp." + suffix;
            }

            /**
             * 覆写父类 contentLength 方法
             * 因为 {@link org.springframework.core.io.AbstractResource#contentLength()}方法会重新读取一遍文件，
             * 而上传文件时，restTemplate 会通过这个方法获取大小。然后当真正需要读取内容的时候，发现已经读完，会报如下错误。
             * <code>
             * java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times
             * at org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96)
             * </code>
             * <p>
             * ref:com.amazonaws.services.s3.model.S3ObjectInputStream#available()
             *
             * @return
             */
            @Override
            public long contentLength() {
                int estimate = length;
                return estimate == 0 ? 1 : estimate;
            }
        };
        // 添加请求参数images，并将Base64编码的图片传入
        map.add("file", inputStreamResource);

        // 构建请求
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map, headers);
        RestTemplate restTemplate = new RestTemplate();
        // 发送请求
        try {
            ResponseEntity<JSONObject> response = restTemplate.postForEntity(restUrl, request, JSONObject.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                JSONObject body = response.getBody();
                HsOrcBody hsOrcBody = body.toJavaObject(HsOrcBody.class);
                return hsOrcBody;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }
    /**
     * 矢量图ocr
     * @param filePath 文件地址
     * @param fileName 文件名
     * @param pageIndex 页码
     * @param vectorgraphText 矢量图信息
     * @param chartPos 坐标信息
     * @return ocr数据
     */
    public static String getPicData(String filePath, String fileName, Integer pageIndex, String vectorgraphText, String chartPos) {
        RestTemplate restTemplate = new RestTemplate();

        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置请求头格式
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        // 构建请求参数
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("pdf_url", filePath);
        jsonObject.put("pdf_name", fileName);
        jsonObject.put("page_index", pageIndex);
        jsonObject.put("vectorgraph_text", vectorgraphText);
        jsonObject.put("chartPos", chartPos);
        HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), headers);
        // 发送请求
        String imageInfo = null;
        try {
            ResponseEntity<String> response = restTemplate.postForEntity(picOcrUrl, request, String.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                imageInfo = response.getBody();
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return imageInfo;
    }
    /**
     * 通过解析数据转换为二维列表
     *
     * @param jsonStr json返回数据
     * @return 二维列表
     */
    public static OcrExcelData getExcelData(String jsonStr) {

        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }

        JSONObject json = JSONObject.parseObject(jsonStr);
        String code = json.getString("code");

        if (!"00".equals(code)) {
            return null;
        }

        OcrExcelData ocrData = new OcrExcelData();

        JSONObject data = json.getJSONObject("data");
        String title = data.getString("title_text");
        ocrData.setTitle(title);
        String remark = data.getString("legend_text");
        ocrData.setRemark(remark);

        JSONObject seriesData = data.getJSONObject("series");
        if (seriesData == null) {
            return null;
        }
        List<List<String>> dataList = xRow(seriesData);

        ocrData.setDataList(dataList);

        return ocrData;
    }
    /**
     * x轴为行模式
     *
     * @return 返回数据
     */
    private static List<List<String>> xRow(JSONObject seriesData) {
        String chartType = seriesData.getString("type");

        JSONArray dataInChart = seriesData.getJSONArray("data_in_chart");
        String signName = seriesData.getString("sign_name");
        // 列
        List<String> columnNames = JSON.parseArray(signName, String.class);
        boolean hasColumnName = !columnNames.isEmpty();

        String xAxisCategories = seriesData.getString("xAxis_categories");
        // 行
        List<String> rowNames = JSON.parseArray(xAxisCategories, String.class);

        List<List<String>> dataList = new ArrayList<>();
        // 设置标题字段
        List<String> colList = new ArrayList<>();
        if (hasColumnName) {
            colList.add("");
            colList.addAll(columnNames);
            dataList.add(colList);
        }

        int dataSize = dataInChart.size();
        // 查询
        List<Map<String, String>> xyMapList = new ArrayList<>();
        for (int i = 0; i < dataSize; i++) {
            JSONArray chartDataList = dataInChart.getJSONArray(i);
            Map<String, String> xyMap = new HashMap<>();

            for (Object chartData : chartDataList) {
                String y = ((JSONObject) chartData).getString("y");
                if ("None".equals(y)) {
                    y = "";
                } else if ("0".equals(y) && "vert_bar".equals(chartType)) {
                    y = "";
                }
                if (isNumber(y)) {
                    BigDecimal yb = new BigDecimal(y);
                    yb = yb.setScale(4, RoundingMode.HALF_UP);
                    y = yb.toString();
                }

                xyMap.put(((JSONObject) chartData).getString("x"), y);
            }
            xyMapList.add(xyMap);
        }

        for (int i = 0; i < rowNames.size(); i++) {
            // 设置数据
            List<String> it = new ArrayList<>();
            it.add(rowNames.get(i));

            if (hasColumnName) {
                for (int j = 0; j < columnNames.size(); j++) {
                    if (dataSize < j) {
                        break;
                    }
                    Map<String, String> xyMap = xyMapList.get(j);
                    it.add(xyMap.getOrDefault(rowNames.get(i), ""));
                }
            } else {
                Map<String, String> xyMap = xyMapList.get(0);
                it.add(xyMap.getOrDefault(rowNames.get(i), ""));
            }

            dataList.add(it);
        }
        return dataList;
    }

    private static boolean isNumber(String number) {
        int index = number.indexOf(".");
        if (index < 0) {
            return NumberUtils.isDigits(number);
        } else {
            String num1 = number.substring(0, index);
            if (number.startsWith("-")) {
                num1 = number.substring(1, index);
            }

            String num2 = number.substring(index + 1);
            return NumberUtils.isDigits(num1) && NumberUtils.isDigits(num2);
        }
    }

    public static String imageAnalyticalStreamV2(InputStream inputStream, String suffix, String ocrResjson) {
        String imageInfo = null;

        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        // 设置请求头格式
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // 构建请求参数
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();

        InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {

            private int length;

            /**
             * 覆写父类方法
             * 如果不重写这个方法，并且文件有一定大小，那么服务端会出现异常
             * {@code The multi-part request contained parameter data (excluding uploaded files) that exceeded}
             *
             * @return
             */
            @Override
            public String getFilename() {
                return "temp." + suffix;
            }

            /**
             * 覆写父类 contentLength 方法
             * 因为 {@link org.springframework.core.io.AbstractResource#contentLength()}方法会重新读取一遍文件，
             * 而上传文件时，restTemplate 会通过这个方法获取大小。然后当真正需要读取内容的时候，发现已经读完，会报如下错误。
             * <code>
             * java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times
             * at org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96)
             * </code>
             * <p>
             * ref:com.amazonaws.services.s3.model.S3ObjectInputStream#available()
             *
             * @return
             */
            @Override
            public long contentLength() {
                int estimate = length;
                return estimate == 0 ? 1 : estimate;
            }
        };
        // 添加请求文件参数
        map.add("file", inputStreamResource);
//        if (StringUtils.isNotBlank(ocrResjson) && !"null".equals(ocrResjson)) {
//            map.add("ocr_res", ocrResjson);
//        }

        // 构建请求
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map, headers);
        RestTemplate restTemplate = new RestTemplate();
        // 发送请求
        try {
            ResponseEntity<JSONObject> response = restTemplate.postForEntity(hsOcrUrl, request, JSONObject.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                imageInfo = JSONObject.toJSONString(response.getBody());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return imageInfo;
    }
}
